package com.bysj.sausys.service.impl;

import com.bysj.sausys.beans.PageQuery;
import com.bysj.sausys.beans.PageResult;
import com.bysj.sausys.beans.Pagination;
import com.bysj.sausys.beans.SubmitTaskBean;
import com.bysj.sausys.exception.ParamException;
import com.bysj.sausys.service.ISysWorkFlowService;
import com.bysj.sausys.util.PropertiesUtil;
import com.bysj.sausys.util.SpringContextUtil;
import com.bysj.sausys.vo.CommentVo;
import com.bysj.sausys.vo.DeploymentVo;
import com.bysj.sausys.vo.ProcessDefinitionVo;
import com.bysj.sausys.vo.TaskVo;
import com.sun.mail.imap.protocol.ID;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.sql.rowset.serial.SerialBlob;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * 工作流的service
 *
 * @author WangZK
 */
@Service("sysWorkFlowService")
public class SysWorkFlowServiceImpl implements ISysWorkFlowService {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    private final String defaultStr = "默认提交";

    /**
     * 获取部署信息
     *
     * @return
     */
    @Override
    public Map<String, Object> getDeployInfo() {
        //查询部署对象的信息，对应表（act_re_deployment）
        //创建部署对象查询
        List<Deployment> deploymentList = repositoryService.createDeploymentQuery()
                .orderByDeploymenTime().asc()
                .list();
        //装载DeploymentVo
        List<DeploymentVo> deploymentVoList = assembleDeploymentVoList(deploymentList);

        //查询流程定义的信息，对应表（act_re_procdef）
        //创建流程定义查询
        List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion().asc()
                .list();
        //装载ProcessDefinitionVo
        List<ProcessDefinitionVo> processDefinitionVoList = assembleProcessDefinitionVoList(processDefinitionList);

        Map<String, Object> map = new HashMap<>(4);
        map.put("deploymentVoList", deploymentVoList);
        map.put("processDefinitionVoList", processDefinitionVoList);

        return map;
    }

    /**
     * 装载DeploymentVo
     *
     * @param deploymentList 部署信息列表
     * @return
     */
    private List<DeploymentVo> assembleDeploymentVoList(List<Deployment> deploymentList) {
        if (deploymentList != null && deploymentList.size() > 0) {
            List<DeploymentVo> deploymentVoList = new ArrayList<>(deploymentList.size());
            DeploymentVo deploymentVo = null;
            for (Deployment deployment : deploymentList) {
                deploymentVo = new DeploymentVo();
                deploymentVo.setId(deployment.getId());
                deploymentVo.setName(deployment.getName());
                deploymentVo.setDeploymentTime(deployment.getDeploymentTime());
                deploymentVoList.add(deploymentVo);
            }
            return deploymentVoList;
        }
        return null;
    }

    /**
     * 装载ProcessDefinitionVo
     *
     * @param processDefinitionList
     * @return
     */
    private List<ProcessDefinitionVo> assembleProcessDefinitionVoList(List<ProcessDefinition> processDefinitionList) {
        if (processDefinitionList != null && processDefinitionList.size() > 0) {
            List<ProcessDefinitionVo> processDefinitionVoList = new ArrayList<>(processDefinitionList.size());
            ProcessDefinitionVo processDefinitionVo = null;
            for (ProcessDefinition processDefinition : processDefinitionList) {
                processDefinitionVo = new ProcessDefinitionVo();
                processDefinitionVo.setId(processDefinition.getId());
                processDefinitionVo.setName(processDefinition.getName());
                processDefinitionVo.setKey(processDefinition.getKey());
                processDefinitionVo.setVersion(processDefinition.getVersion());
                processDefinitionVo.setResourceName(processDefinition.getResourceName());
                processDefinitionVo.setDiagramResourceName(processDefinition.getDiagramResourceName());
                processDefinitionVo.setDeploymentId(processDefinition.getDeploymentId());
                processDefinitionVoList.add(processDefinitionVo);
            }
            return processDefinitionVoList;
        }
        return null;
    }

    /**
     * 发布新的流程定义
     *
     * @param zipFile
     * @param path
     * @param processDefinitionName
     */
    @Override
    public void saveNewDeploy(MultipartFile zipFile, String path, String processDefinitionName) {

        File fileDir = new File(path);
        if (!fileDir.exists()) {
            fileDir.setWritable(true);
            if (!fileDir.mkdirs()) {
                throw new RuntimeException("创建文件夹失败");
            }
        }

        File targetFile = new File(path, zipFile.getOriginalFilename());

        try {
            zipFile.transferTo(targetFile);

            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(targetFile));

            //创建部署对象,添加部署名称
            repositoryService.createDeployment()
                    .name(processDefinitionName)
                    .addZipInputStream(zipInputStream)
                    .deploy();

            zipInputStream.close();
            boolean success = (targetFile.delete());
            if (success) {
                System.out.println("Successfully deleted empty directory: " + targetFile.getPath());
            } else {
                System.out.println("Failed to delete empty directory: " + targetFile.getPath());
            }
        } catch (Exception e) {
            throw new RuntimeException("发布流程失败");
        }
    }

    /**
     * 删除流程定义
     *
     * @param deploymentId
     */
    @Override
    public void deleteProcessDefinitionByDeploymentId(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, true);
    }

    /**
     * 获取图片
     *
     * @param deploymentId
     * @param diagramResourceName
     * @return
     */
    @Override
    public InputStream findImageInputStream(String deploymentId, String diagramResourceName) {
        return repositoryService.getResourceAsStream(deploymentId, diagramResourceName);
    }

    /**
     * 启动流程
     *
     * @param key
     * @param objId
     * @param username
     * @return
     */
    @Override
    public String saveStartProcess(String key, String objId, String username) {
        //设置流程变量
        Map<String, Object> variables = new HashMap<>(4);
        variables.put("userId", username);
        variables.put("objId", objId);

        //启动流程实例
        return runtimeService.startProcessInstanceByKey(key, objId, variables).getId();
    }

    /**
     * 获取任务列表
     * 1.创建任务查询，根据用户名查询
     * 2.查询任务总数
     * 3.结果总数大于1，再查询任务详细信息
     * 4.对任务详细信息进行装载
     * 5.封装返回结果
     *
     * @param pageQuery
     * @param username
     * @return
     */
    @Override
    public PageResult<TaskVo> findTaskListByUsername(PageQuery pageQuery, String username) {
        //1.创建任务查询,指定个人任务查询
        TaskQuery taskQuery = taskService.createTaskQuery()
                .taskCandidateOrAssigned(username);

        //2.查询任务总数
        int count = (int) taskQuery.count();

        if (count > 0) {
            //3.查询任务的详细信息
            List<Task> taskList = taskQuery.listPage(pageQuery.getOffset(), pageQuery.getPageSize());
            //4.对任务的详细信息进行装载
            List<TaskVo> taskVoList = assembleTaskVoList(taskList);

            //5.封装返回结果
            //封装分页信息
            Pagination pagination = new Pagination();
            pagination.setTotal(count);
            pagination.setPageSize(pageQuery.getPageSize());
            pagination.setCurrentPage(pageQuery.getPageNo());

            //封装返回结果并返回
            PageResult<TaskVo> result = new PageResult<>();
            result.setPagination(pagination);
            result.setList(taskVoList);
            return result;
        }
        return new PageResult<>();
    }

    /**
     * 装载TaskVo
     *
     * @param taskList
     * @return
     */
    private List<TaskVo> assembleTaskVoList(List<Task> taskList) {
        if (taskList != null && taskList.size() > 0) {
            List<TaskVo> taskVoList = new ArrayList<>(taskList.size());
            TaskVo taskVo = null;
            for (Task task : taskList) {
                taskVo = new TaskVo();
                taskVo.setId(task.getId());
                taskVo.setName(task.getName());
                taskVo.setCreateTime(task.getCreateTime());
                taskVo.setAssignee(task.getAssignee());
                taskVo.setProcessDefinitionKey(repositoryService.getProcessDefinition(getTaskByTaskId(task.getId()).getProcessDefinitionId()).getKey());
                taskVoList.add(taskVo);
            }
            return taskVoList;
        }
        return null;
    }

    /**
     * 获取连线信息和历史审核人信息
     *
     * @param taskId
     */
    @Override
    public Map getAuditInfo(String taskId) {

        Task task = findTaskById(taskId);
        if (task == null) {
            throw new ParamException("获取任务失败");
        }

        // 获取连线信息
        List<String> outcomeList = findOutComeListByTaskId(taskId);
        // 查询所有历史审核人的信息
        List<CommentVo> commentVoList = findCommentByTaskId(taskId);

        Map<String, Object> map = new HashMap<>(4);
        map.put("outcomeList", outcomeList);
        map.put("commentVoList", commentVoList);

        return map;
    }

    /**
     * 根据任务id获取业务数据
     *
     * @param taskId
     * @return
     */
    @Override
    public int getBusinessIdByTaskId(String taskId) {

        //2.使用任务对象Task获取流程实例ID
        String processInstanceId = getTaskByTaskId(taskId).getProcessInstanceId();

        //3.使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        //4.使用流程实例对象回去BUSINESS_KEY
        String businessKey = processInstance.getBusinessKey();

        //5.获取BUSINESS_KEY对应的主键ID，使用主键ID，查询请假单对象
        String id = null;
        if (StringUtils.isNotBlank(businessKey)) {
            //截取字符串，取business_key小数点的第2个值
            id = businessKey.split("\\.")[1];
        }
        return Integer.parseInt(id);
    }

    /**
     * 获取连线信息
     *
     * @param taskId
     * @return
     */
    @Override
    public List<String> findOutComeListByTaskId(String taskId) {
        //返回存放连线的名称集合
        List<String> list = new ArrayList<>();

        //2.获取流程定义ID
        String processDefinitionId = getTaskByTaskId(taskId).getProcessDefinitionId();

        //3.查询ProcessDefinitionEntity对象
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) this.repositoryService.getProcessDefinition(processDefinitionId);

        //4.使用任务对象Task获取流程实例ID
        String processInstanceId = getTaskByTaskId(taskId).getProcessInstanceId();

        //5.使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
        ProcessInstance processInstance = this.runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        //6.获取当前活动的id
        String activityId = processInstance.getActivityId();

        //7.获取当前的活动
        ActivityImpl activity = processDefinitionEntity.findActivity(activityId);

        //8.获取当前活动完成之后连线的名称
        List<PvmTransition> pvmTransitions = activity.getOutgoingTransitions();
        if (pvmTransitions != null && pvmTransitions.size() > 0) {
            for (PvmTransition pvm : pvmTransitions) {
                String name = (String) pvm.getProperty("name");
                if (StringUtils.isNotBlank(name)) {
                    list.add(name);
                } else {
                    list.add("默认提交");
                }
            }
        }
        return list;
    }

    /**
     * 获取批注信息，传递的是当前任务ID，获取历史任务ID对应的批注
     *
     * @param taskId
     * @return
     */
    @Override
    public List<CommentVo> findCommentByTaskId(String taskId) {
        //根据任务ID获取流程实例ID
        String processInstanceId = getTaskByTaskId(taskId).getProcessInstanceId();
        //获取批注信息
        List<Comment> commentList = taskService.getProcessInstanceComments(processInstanceId);
        //装载CommentVo
        List<CommentVo> commentVoList = assembleCommentVoList(commentList);
        return commentVoList;
    }

    /**
     * 装载CommentVo
     *
     * @param commentList
     * @return
     */
    private List<CommentVo> assembleCommentVoList(List<Comment> commentList) {
        if (commentList != null && commentList.size() > 0) {
            List<CommentVo> commentVoList = new ArrayList<>(commentList.size());
            CommentVo commentVo = null;
            for (Comment comment : commentList) {
                commentVo = new CommentVo();
                commentVo.setTime(comment.getTime());
                commentVo.setUserId(comment.getUserId());
                commentVo.setFullMessage(comment.getFullMessage());
                commentVoList.add(commentVo);
            }
            return commentVoList;
        }
        return null;
    }

    /**
     * 根据任务ID获取任务对象
     *
     * @param taskId 任务id
     * @return
     */
    @Override
    public Task findTaskById(String taskId) {
        //使用任务ID查询
        Task task = this.taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();

        return task;
    }

    /**
     * 根据任务ID获取任务对象
     *
     * @param taskId 任务id
     * @return
     */
    private Task getTaskByTaskId(String taskId) {
        //使用任务ID查询
        Task task = this.taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        return task;
    }


    /**
     * 在完成之前，添加一个批注信息，向act_hi_comment表中添加数据，用于记录对当前申请人的一些审核信息
     * 2.通过任务id获取流程实例ID
     * 4.添加批注
     * 5.设置流程变量
     * 6.完成任务
     * 当任务完成之后，需要指定下一个任务的办理人（默认已经使用类实现）
     *
     * @param submitTaskBean
     * @param username
     * @param variables
     * @return
     */
    @Override
    public String saveSubmitTask(SubmitTaskBean submitTaskBean, String username, Map variables) {
        //根据任务ID获取流程实例ID
        String processInstanceId = getTaskByTaskId(submitTaskBean.getTaskId()).getProcessInstanceId();

        //拾取任务
        String assignee = getTaskByTaskId(submitTaskBean.getTaskId()).getAssignee();
        if (assignee == null || "".equals(assignee)) {
            taskService.claim(submitTaskBean.getTaskId(), username);
        }

        //添加批注之前使用Authentication.setAuthenticatedUserId(String authenticatedUserId)添加当前任务的审核人
        Authentication.setAuthenticatedUserId(username);
        taskService.addComment(submitTaskBean.getTaskId(), processInstanceId, submitTaskBean.getComment());

        /*
        如果连线的名称是“默认提交”，那么就不需要设置，如果不是，就需要设置流程变量
        在完成任务之前，设置流程变量，按照连线的名称，去完成任务
         */
        if (StringUtils.isNotBlank(submitTaskBean.getOutcome()) && !this.defaultStr.equals(submitTaskBean.getOutcome())) {
            variables.put("outcome", submitTaskBean.getOutcome());
        }

        //使用任务ID，完成当前的个人任务，同时设置流程变量
        taskService.complete(submitTaskBean.getTaskId(), variables);

        return processInstanceId;
    }

    /**
     * 根据任务id获取流程实例，判断流程是否已经结束，结束返回true，未结束返回false
     *
     * @param processInstanceId
     * @return
     */
    @Override
    public boolean checkProcessIsEnd(String processInstanceId) {
        //使用流程实例ID查询
        ProcessInstance processInstance = this.runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        //如果流程结束，更新请假单表的状态从1变成2（审核中-->审核完成）
        if (processInstance == null) {
            return true;
        }
        return false;
    }

    /**
     * 使用objId查询批注
     *
     * @param objId
     * @return
     */
    @Override
    public Map findCommentByObjId(String objId) {
        //使用流程变量的名称和流程变量的值查询
        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery()
                .variableValueEquals("objId", objId)
                .singleResult();

        String processInstanceId = historicVariableInstance.getProcessInstanceId();

        List<Comment> commentList = this.taskService.getProcessInstanceComments(processInstanceId);
        //装载CommentVo
        List<CommentVo> commentVoList = assembleCommentVoList(commentList);

        Map<String, Object> map = new HashMap<>(4);
        map.put("commentVoList", commentVoList);
        return map;
    }

    /**
     * 查看当前活动，获取当前活动对应的坐标，将值放到Map中
     *
     * @param taskId
     * @return
     */
    @Override
    public Map<String, Object> findCoordingByTaskId(String taskId) {
        //存放坐标
        Map<String, Object> map = new HashMap<>(8);

        //获取流程定义ID
        String processDefinitionId = getTaskByTaskId(taskId).getProcessDefinitionId();

        //查询ProcessDefinitionEntity对象
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);

        //获取流程实例ID
        String processInstanceId = getTaskByTaskId(taskId).getProcessInstanceId();

        //使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        //获取当前活动的id
        String activityId = processInstance.getActivityId();

        //获取当前的活动
        ActivityImpl activity = processDefinitionEntity.findActivity(activityId);

        map.put("x", activity.getX());
        map.put("y", activity.getY());
        map.put("height", activity.getHeight());
        map.put("width", activity.getWidth());

        return map;
    }

    /**
     * 获取当前图片的相关信息
     *
     * @param taskId
     * @param request
     * @return
     */
    @Override
    public Map<String, Object> getCurrentImageInfo(String taskId, HttpServletRequest request) {

        Map<String, Object> result = new HashMap<>(4);

        Map<String, Object> map = findCoordingByTaskId(taskId);
        result.put("acs", map);

        String processDefinitionId = getTaskByTaskId(taskId).getProcessDefinitionId();
        //获取流程定义，使用流程定义ID查询
        ProcessDefinition processDefinition = this.repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();

        //获取部署ID和图片名称
        String deploymentId = processDefinition.getDeploymentId();
        String diagramResourceName = processDefinition.getDiagramResourceName();

        String url = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() +
                "/sys/workflow/viewImage?deploymentId=" + deploymentId + "&diagramResourceName=" + diagramResourceName;
        result.put("url", url);

        return result;
    }
}
